<?php

Raise::load('core.io.RaiseStream');
Raise::load('core.reflection.RaiseVariable');

/**
 * RaiseDataStream class
 * Provide data read and write streaming to a physical stream
 *
 * @author Sam-Mauris Yong / hellclanner at live dot com
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package phpraise.core.io
 * @since 1.2
 */
class RaiseDataStream extends RaiseStream {

    /**
     * Create a new RaiseDataStream wrapping an existing stream
     * @param RaiseStream $stream
     */
    function __construct($stream) {
        if(!RaiseVariable::isSubclass($stream, 'RaiseStream')){
            throw new RaiseInvalidArgumentException('RaiseStream was expected for $stream');
        }
        $this->parentStream = $stream;
    }

    /**
     * Close the stream and its parent stream
     */
    public function close() {
        $this->parentStream->close();
    }

    /**
     * Get the length of the parent stream
     * @return integer
     */
    public function length() {
        return $this->parentStream->length();
    }

    /**
     * Determine if the stream can be read
     * @return boolean
     */
    public function isReadable() {
        return true;
    }

    /**
     * Determine if the seek operation can be done on the stream
     * @return boolean
     */
    public function isSeekable() {
        return true;
    }

    /**
     * Determine if the stream can be written
     * @return boolean
     */
    public function isWriteable() {
        return true;
    }

    /**
     * Read a string from the stream starting from the pointer position and move the pointer by $length
     * @param int $length The number of bytes to read from the stream
     * @return string The string value from the stream
     */
    public function read($length) {
        return $this->parentStream->read($length);
    }

    /**
     * Read a single byte from the stream starting from the pointer position and move the pointer by 1
     * @return integer The value of the byte
     */
    public function readByte() {
        return $this->parentStream->readByte();
    }

    /**
     * Move the pointer location to a specific location
     * @param integer $position The new position of the pointer
     */
    public function seek($position) {
        $this->parentStream->seek($position);
    }

    /**
     * Write a string to the stream at the pointer location and move the pointer by length of $str
     * @param string $str The string value
     */
    public function write($str) {
        $this->parentStream->write($str);
        return strlen($str);
    }

    /**
     * Write a single byte to the stream at the pointer location and move the pointer by 1
     * @param integer $i A byte value from 0-255
     */
    public function writeByte($i) {
        $this->parentStream->writeByte($i);
        return 1;
    }

    /**
     * Write an integer value into the stream at the pointer location and move the pointer by 4 bytes
     * @param integer $i The integer to write
     * @return integer The number of bytes written (i.e. 4 bytes)
     */
    public function writeInteger($i){
        $s = pack('l', $i);
        $this->parentStream->write($s);
        return strlen($s);
    }

    /**
     * Read an integer value from the stream at the pointer location and move the pointer by 4 bytes
     * @return integer
     */
    public function readInteger(){
        $a = unpack('l', $this->parentStream->read(4));
        return current($a);
    }

    /**
     * Write a double value into the stream at the pointer location and move the pointer by 8 bytes
     * @param double $d The double to write
     * @return integer The number of bytes written (i.e. 8 bytes)
     */
    public function writeDouble($d){
        $s = pack('d', $d);
        $this->parentStream->write($s);
        return strlen($s);
    }

    /**
     * Read a double value from the stream at the pointer location and move the pointer by 8 bytes
     * @return double
     */
    public function readDouble(){
        $a = unpack('d', $this->parentStream->read(8));
        return current($a);
    }

    /**
     * Write a boolean value into the stream at the pointer location and move the pointer by 1 byte
     * @param boolean $b The boolean value to write
     * @return integer The number of bytes written (i.e. 1 byte)
     */
    public function writeBoolean($b){
        $this->parentStream->write($b ? '1' : '0');
        return 1;
    }

    /**
     * Read a boolean value from the stream at the pointer location and move the pointer by 1 byte
     * @return boolean
     */
    public function readBoolean(){
        return $this->parentStream->read(1) == '1' ? true : false;
    }

    /**
     * Write a string value into the stream at the pointer location and move the pointer by the string length + 4 bytes
     * Note that 4 bytes are added in front of the string value to determine the length of the string for use when reading
     * @param string $s The string value to write
     * @return integer The number of bytes written (i.e. string length + 4 byte)
     */
    public function writeString($s){
        $this->writeInteger(strlen($s));
        $this->parentStream->write($s);
        return strlen($s);
    }

    /**
     * Read a string value from the stream at the pointer location and move the pointer by the string length + 4 bytes
     * @return string
     */
    public function readString(){
        $i = $this->readInteger();
        $s = $this->parentStream->read($i);
        return $s;
    }

    /**
     * Write a object into the stream at the pointer location and move the pointer by the serialized object string length + 4 bytes
     * Note that 4 bytes are added in front of the string value to determine the length of the string for use when reading
     * @param object $o The object to write
     * @return integer The number of bytes written (i.e. serialized object string length + 4 byte)
     */
    public function writeObject($o){
        $s = serialize($o);        
        return $this->writeString($s);
    }

    /**
     * Read a string value from the stream at the pointer location and move the pointer by the string length + 4 bytes
     * @return string
     */
    public function readObject(){
        $s = $this->readString();
        return unserialize($s);
    }

    /**
     * Get or set the pointer position of the underlying stream
     * @param integer $p (optional) The new position to set to
     * @return integer
     */
    public function position($p = null) {
        if(func_num_args() == 1){
            $this->parentStream->position($p);
        }
        return $this->parentStream->position();
    }


}
